package com.yeskery.nut.bean;

import com.yeskery.nut.annotation.bean.Autowired;
import com.yeskery.nut.annotation.bean.Qualifier;
import com.yeskery.nut.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Parameter;

/**
 * 应用上下文bean持有对象
 * @author sprout
 * 2022-06-21 12:16
 */
public class ApplicationContextBeanHolder {

    /** 应用上下文 */
    private final ApplicationContext applicationContext;

    /**
     * 构建应用上下文bean持有对象
     * @param applicationContext 应用上下文
     */
    public ApplicationContextBeanHolder(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * 从方法参数中获取持有的bean对象
     * @param parameter 参数对象
     * @return 持有的bean对象
     */
    public Object getHoldBean(Parameter parameter) {
        return doGetHoldBean(parameter.getAnnotation(Autowired.class), parameter.getAnnotation(Qualifier.class), parameter.getType());
    }

    /**
     * 从字段对象中获取持有的bean对象
     * @param field 字段对象
     * @return 持有的bean对象
     */
    public Object getHoldBean(Field field) {
        return doGetHoldBean(field.getAnnotation(Autowired.class), field.getAnnotation(Qualifier.class), field.getType());
    }

    /**
     * 从方法参数中获取持有的bean对象
     * @param parameter 参数对象
     * @param clazz  bean类型对象
     * @param <T> bean类型
     * @return 持有的bean对象
     */
    @SuppressWarnings("unchecked")
    public <T> T getHoldBean(Parameter parameter, Class<T> clazz) {
        return (T) getHoldBean(parameter);
    }

    /**
     * 从字段对象中获取持有的bean对象
     * @param field 字段对象
     * @param clazz bean类型对象
     * @param <T> bean类型
     * @return 持有的bean对象
     */
    @SuppressWarnings("unchecked")
    public <T> T getHoldBean(Field field, Class<T> clazz) {
        return (T) getHoldBean(field);
    }

    /**
     * 获取持有的bean对象
     * @param autowired Autowired对象
     * @param qualifier Qualifier对象
     * @param type bean类型
     * @return 持有的bean对象
     */
    private Object doGetHoldBean(Autowired autowired, Qualifier qualifier, Class<?> type) {
        boolean required = autowired == null || autowired.required();
        String beanName = qualifier == null ? null : StringUtils.isEmpty(qualifier.value()) ? null : qualifier.value();
        try {
            if (beanName == null) {
                return applicationContext.getBean(type);
            } else {
                return applicationContext.getBean(beanName);
            }
        } catch (NoSuchBeanException e) {
            if (required) {
                throw e;
            }
            return null;
        }
    }
}
